﻿using System.Numerics;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;

namespace SimpleVector
{
    public unsafe struct SuperVector : IEquatable<SuperVector>
    {
        private readonly static int _vsize;
        static SuperVector()
        {
            bool status = Sse2.IsSupported;
            _vsize = Vector256<int>.Count;
        }
        private readonly int[] _value;
        public SuperVector(int[] value)
        {
            _value = value;
        }
        public SuperVector(SuperVector value)
        {
            _value = new int[value.Value.Length];
            Array.Copy(value.Value, _value, value.Value.Length);
        }
        public SuperVector(int value, int size)
        {
            _value = new int[size];
            Array.Fill(_value, value);
        }
        public int[] Value => _value;
        public static SuperVector Empty = new SuperVector();
        public int Sum()
        {
            int sum = 0;
            fixed (int* ptrLeft = Value)
            {
                int limit = Value.Length - (Value.Length & 7);
                int i = 0;
                if (Value.Length >= _vsize << 1)
                {
                    Vector256<int> sumV = Vector256<int>.Zero;
                    for (; i < limit; i += _vsize)
                    {
                        sumV = Avx2.Add(sumV, *(Vector256<int>*)(ptrLeft + i));
                    }
                    sum = Vector256.Sum(sumV);
                }
                for (; i < Value.Length; i++)
                {
                    sum += *(ptrLeft + i);
                }
            }
            return sum;
        }
        public double Average() => Sum() / (double)Value.Length;
        public double Variance()
        {
            double avg = Average();
            double sum = 0;
            fixed (int* ptrLeft = Value)
            {
                int limit = Value.Length - (Value.Length & 3);
                int i = 0;
                if (Value.Length >= _vsize << 1)
                {
                    Vector256<double> sumV = Vector256<double>.Zero;
                    Vector256<double> avgV = Vector256.Create(avg);
                    int vsize = Vector256<double>.Count;
                    for (; i < limit; i += vsize)
                    {
                        var temp = Avx2.ConvertToVector256Double(*(Vector128<int>*)(ptrLeft + i)) - avgV;
                        sumV = Avx2.Add(sumV, Avx2.Multiply(temp, temp));
                    }
                    sum = Vector256.Sum(sumV);
                }
                for (; i < Value.Length; i++)
                {
                    sum += Math.Pow(*(ptrLeft + i) - avg, 2);
                }
            }
            return sum / Value.Length;
        }
        public double StandardDeviation() => Math.Sqrt(Variance());
        public int Max()
        {
            fixed (int* ptrLeft = Value)
            {
                int limit = Value.Length - (Value.Length & 7);
                int i = 1;
                int max = *ptrLeft;
                if (Value.Length >= _vsize << 1)
                {
                    Vector256<int> maxV = *(Vector256<int>*)ptrLeft;
                    i = _vsize;
                    for (; i < limit; i += _vsize)
                    {
                        maxV = Avx2.Max(maxV, *(Vector256<int>*)(ptrLeft + i));
                    }

                    int* ptr = (int*)&maxV;
                    for (int j = 0; j < _vsize; j++)
                    {
                        int t = *(ptr + j);
                        if (max < t)
                            max = t;
                    }
                }
                for (; i < Value.Length; i++)
                {
                    int t = *(ptrLeft + i);
                    if (max < t)
                        max = t;
                }
                return max;
            }
        }
        public int Min()
        {
            fixed (int* ptrLeft = Value)
            {
                int min = *ptrLeft;
                int limit = Value.Length - (Value.Length & 7);
                int i = 1;
                if (Value.Length >= _vsize << 1)
                {
                    Vector256<int> minV = *(Vector256<int>*)ptrLeft;
                    i = _vsize;
                    for (; i < limit; i += _vsize)
                    {
                        minV = Avx2.Min(minV, *(Vector256<int>*)(ptrLeft + i));
                    }

                    int* ptr = (int*)&minV;
                    for (int j = 0; j < _vsize; j++)
                    {
                        int t = *(ptr + j);
                        if (min > t)
                            min = t;
                    }
                }
                for (; i < Value.Length; i++)
                {
                    int t = *(ptrLeft + i);
                    if (min > t)
                        min = t;
                }
                return min;
            }
        }
        public bool Any(int target)
        {
            fixed (int* ptrLeft = Value)
            {
                int limit = Value.Length - (Value.Length & 7);
                int i = 1;
                if (Value.Length >= _vsize << 1)
                {
                    Vector256<int> targetV = Vector256.Create(target);
                    i = _vsize;
                    for (; i < limit; i += _vsize)
                    {
                        var t = *(Vector256<int>*)(ptrLeft + i);
                        if (Vector256.EqualsAny(*(Vector256<int>*)(ptrLeft + i), targetV)) return true;
                    }
                }
                for (; i < Value.Length; i++)
                {
                    if (*(ptrLeft + i) == target) return true;
                }
                return false;
            }
        }
        public double[] Sqrt()
        {
            double[] result = new double[this.Value.Length];
            fixed (int* ptrLeft = this.Value)
            {
                fixed(double* resultPtr= result)
                {
                    int limit = this.Value.Length - (this.Value.Length & 3);
                    int i = 0;
                    int visze = Vector256<double>.Count;
                    Vector256<int> allOnes = Vector256.Create(1);
                    for (; i < limit; i += visze)
                    {
                        Avx2.Store(resultPtr + i, Avx2.Sqrt(Avx2.ConvertToVector256Double(*(Vector128<int>*)(ptrLeft + i))));
                    }
                    for (; i < this.Value.Length; i++)
                    {
                        *(resultPtr + i) = Math.Sqrt(*(ptrLeft + i));
                    }
                }
                
            }
            return result;
        }
        public SuperVector Sqrt2Vector() => Sqrt().Select(t => (int)t).ToArray();
        public SuperVector Sort()
        {
            Array.Sort(this.Value);
            return this;
        }
        public override bool Equals(object? obj)
        {
            if (obj == null) return false;
            if (obj is SuperVector other) return Equals(this, other);
            return false;
        }
        public bool Equals(SuperVector other) => Equals(this, other);
        public bool Equals(int[] other) => Equals((SuperVector)other);
        private static bool Equals(SuperVector left, SuperVector right)
        {
            if (left.Value.Length != right.Value.Length) return false;
            fixed (int* ptrLeft = left.Value, ptrRight = right.Value)
            {
                int limit = left.Value.Length - (left.Value.Length & 7);
                int i = 0;
                Vector256<int> one = Vector256.Create(-1);
                for (; i < limit; i += _vsize)
                {
                    if (one != Avx2.CompareEqual(*(Vector256<int>*)(ptrLeft + i), *(Vector256<int>*)(ptrRight + i))) return false;
                }
                for (; i < left.Value.Length; i++)
                {
                    if (*(ptrLeft + i) != *(ptrRight + i)) return false;
                }
            }
            return true;
        }
        public override int GetHashCode()
        {
            int code = 0;
            fixed (int* ptr = _value)
            {
                const int size = 7;
                int limit = _value.Length - _value.Length % size;
                int i = 0;
                for (; i < limit; i += size)
                {
                    int* temp = ptr + i;
                    code = HashCode.Combine(code, *ptr, *(ptr + 1), *(ptr + 2), *(ptr + 3), *(ptr + 4), *(ptr + 5), *(ptr + 6));
                }
                for (; i < _value.Length; i++)
                {
                    code = HashCode.Combine(code, *(ptr + i));
                }
            }
            return code;
        }
        public static SuperVector BitwiseNot(SuperVector other)
        {
            int[] result = new int[other.Value.Length];
            fixed (int* ptrLeft = other.Value, resultPtr = result)
            {
                int limit = other.Value.Length - (other.Value.Length & 7);
                int i = 0;
                Vector256<int> allOnes = Vector256.Create(1);
                for (; i < limit; i += _vsize)
                {
                    Avx2.Store(resultPtr + i, ~*(Vector256<int>*)(ptrLeft + i) + allOnes);
                }
                for (; i < other.Value.Length; i++)
                {
                    *(resultPtr + i) = ~*(ptrLeft + i) + 1;
                }
            }
            return result;
        }
        public static SuperVector operator ~(SuperVector other) => BitwiseNot(other);
        public static SuperVector operator &(SuperVector left, SuperVector right)
        {
            if (left.Value.Length != right.Value.Length)
                throw new ArgumentException("向量大小不一致");

            int[] result = new int[left.Value.Length];
            fixed (int* ptrLeft = left.Value, ptrRight = right.Value, resultPtr = result)
            {
                int limit = left.Value.Length - (left.Value.Length & 7);
                int i = 0;
                for (; i < limit; i += _vsize)
                {
                    Avx2.Store(resultPtr + i,Avx2.And(*(Vector256<int>*)(ptrLeft + i) , *(Vector256<int>*)(ptrRight + i)));
                }
                for (; i < left.Value.Length; i++)
                {
                    *(resultPtr + i) = *(ptrLeft + i) & *(ptrRight + i);
                }
            }
            return result;
        }
        public static SuperVector operator &(SuperVector left, int right)
        {
            int[] result = new int[left.Value.Length];
            fixed (int* ptrLeft = left.Value, resultPtr = result)
            {
                int limit = left.Value.Length - (left.Value.Length & 7);
                int i = 0;
                Vector256<int> targetV = Vector256.Create(right);
                for (; i < limit; i += _vsize)
                {
                    Avx2.Store(resultPtr + i, Avx2.And(*(Vector256<int>*)(ptrLeft + i), targetV));
                }
                for (; i < left.Value.Length; i++)
                {
                    *(resultPtr + i) = *(ptrLeft + i) & right;
                }
            }
            return result;
        }
        public static SuperVector operator &(int left, SuperVector right) => right & left;
        public static SuperVector operator |(SuperVector left, SuperVector right)
        {
            if (left.Value.Length != right.Value.Length)
                throw new ArgumentException("向量大小不一致");

            int[] result = new int[left.Value.Length];
            fixed (int* ptrLeft = left.Value, ptrRight = right.Value, resultPtr = result)
            {
                int limit = left.Value.Length - (left.Value.Length & 7);
                int i = 0;
                for (; i < limit; i += _vsize)
                {
                    Avx2.Store(resultPtr + i, *(Vector256<int>*)(ptrLeft + i) | *(Vector256<int>*)(ptrRight + i));
                }
                for (; i < left.Value.Length; i++)
                {
                    *(resultPtr + i) = *(ptrLeft + i) | *(ptrRight + i);
                }
            }
            return result;
        }
        public static SuperVector operator |(SuperVector left, int right)
        {
            int[] result = new int[left.Value.Length];
            fixed (int* ptrLeft = left.Value, resultPtr = result)
            {
                int limit = left.Value.Length - (left.Value.Length & 7);
                int i = 0;
                Vector256<int> targetV = Vector256.Create(right);
                for (; i < limit; i += _vsize)
                {
                    Avx2.Store(resultPtr + i, Avx2.Or(*(Vector256<int>*)(ptrLeft + i), targetV));
                }
                for (; i < left.Value.Length; i++)
                {
                    *(resultPtr + i) = *(ptrLeft + i) | right;
                }
            }
            return result;
        }
        public static SuperVector operator |(int left, SuperVector right) => right | left;
        public static SuperVector operator ^(SuperVector left, SuperVector right)
        {
            if (left.Value.Length != right.Value.Length)
                throw new ArgumentException("向量大小不一致");

            int[] result = new int[left.Value.Length];
            fixed (int* ptrLeft = left.Value, ptrRight = right.Value, resultPtr = result)
            {
                int limit = left.Value.Length - (left.Value.Length & 7);
                int i = 0;
                for (; i < limit; i += _vsize)
                {
                    Avx2.Store(resultPtr + i, *(Vector256<int>*)(ptrLeft + i) ^ *(Vector256<int>*)(ptrRight + i));
                }
                for (; i < left.Value.Length; i++)
                {
                    *(resultPtr + i) = *(ptrLeft + i) ^ *(ptrRight + i);
                }
            }
            return result;
        }
        public static SuperVector operator >>(SuperVector other, int shiftCount)
        {
            int[] result = new int[other.Value.Length];
            fixed (int* ptrLeft = other.Value, resultPtr = result)
            {
                int limit = other.Value.Length - (other.Value.Length & 7);
                int i = 0;
                for (; i < limit; i += _vsize)
                {
                    Avx2.Store(resultPtr + i, ~*(Vector256<int>*)(ptrLeft + i) >> shiftCount);
                }
                for (; i < other.Value.Length; i++)
                {
                    *(resultPtr + i) = ~*(ptrLeft + i) >> shiftCount;
                }
            }
            return result;
        }
        public static SuperVector operator >>>(SuperVector other, int shiftCount)
        {
            int[] result = new int[other.Value.Length];
            fixed (int* ptrLeft = other.Value, resultPtr = result)
            {
                int limit = other.Value.Length - (other.Value.Length & 7);
                int i = 0;
                for (; i < limit; i += _vsize)
                {
                    Avx2.Store(resultPtr + i, ~*(Vector256<int>*)(ptrLeft + i) >>> shiftCount);
                }
                for (; i < other.Value.Length; i++)
                {
                    *(resultPtr + i) = ~*(ptrLeft + i) >>> shiftCount;
                }
            }
            return result;
        }
        public static SuperVector operator <<(SuperVector other, int shiftCount)
        {
            int[] result = new int[other.Value.Length];
            fixed (int* ptrLeft = other.Value, resultPtr = result)
            {
                int limit = other.Value.Length - (other.Value.Length & 7);
                int i = 0;
                for (; i < limit; i += _vsize)
                {
                    Avx2.Store(resultPtr + i, ~*(Vector256<int>*)(ptrLeft + i) << shiftCount);
                }
                for (; i < other.Value.Length; i++)
                {
                    *(resultPtr + i) = ~*(ptrLeft + i) << shiftCount;
                }
            }
            return result;
        }
        public static SuperVector operator +(SuperVector other) => new SuperVector(other);
        public static SuperVector operator -(SuperVector other)
        {
            int[] result = new int[other.Value.Length];
            fixed (int* ptrLeft = other.Value, resultPtr = result)
            {
                int limit = other.Value.Length - (other.Value.Length & 7);
                int i = 0;
                Vector256<int> allOnes = Vector256.Create(1);
                for (; i < limit; i += _vsize)
                {
                    Avx2.Store(resultPtr + i, -*(Vector256<int>*)(ptrLeft + i));
                }
                for (; i < other.Value.Length; i++)
                {
                    *(resultPtr + i) = -Math.Abs(*(ptrLeft + i));
                }
            }
            return result;
        }
        public static SuperVector operator +(SuperVector left, SuperVector right)
        {
            if (left.Value.Length != right.Value.Length)
                throw new ArgumentException("向量大小不一致");

            int[] result = new int[left.Value.Length];
            fixed (int* ptrLeft = left.Value, ptrRight = right.Value, resultPtr = result)
            {
                int limit = left.Value.Length - (left.Value.Length & 7);
                int i = 0;
                for (; i < limit; i += _vsize)
                {
                    Avx2.Store(resultPtr + i, *(Vector256<int>*)(ptrLeft + i) + *(Vector256<int>*)(ptrRight + i));
                }
                for (; i < left.Value.Length; i++)
                {
                    *(resultPtr + i) = *(ptrLeft + i) + *(ptrRight + i);
                }
            }
            return result;
        }
        public static SuperVector operator +(SuperVector left, int right)
        {
            int[] result = new int[left.Value.Length];
            fixed (int* ptrLeft = left.Value, resultPtr = result)
            {
                int limit = left.Value.Length - (left.Value.Length & 7);
                int i = 0;
                Vector256<int> target = Vector256.Create(right);
                for (; i < limit; i += _vsize)
                {
                    Avx2.Store(resultPtr + i, *(Vector256<int>*)(ptrLeft + i) + target);
                }
                for (; i < left.Value.Length; i++)
                {
                    *(resultPtr + i) = *(ptrLeft + i) + right;
                }
            }
            return result;
        }
        public static SuperVector operator +(int left, SuperVector right) => right + left;
        public static SuperVector operator -(SuperVector left, SuperVector right)
        {
            if (left.Value.Length != right.Value.Length)
                throw new ArgumentException("向量大小不一致");

            int[] result = new int[left.Value.Length];
            fixed (int* ptrLeft = left.Value, ptrRight = right.Value, resultPtr = result)
            {
                int limit = left.Value.Length - (left.Value.Length & 7);
                int i = 0;
                for (; i < limit; i += _vsize)
                {
                    Avx2.Store(resultPtr + i, *(Vector256<int>*)(ptrLeft + i) - *(Vector256<int>*)(ptrRight + i));
                }
                for (; i < left.Value.Length; i++)
                {
                    *(resultPtr + i) = *(ptrLeft + i) - *(ptrRight + i);
                }
            }
            return result;
        }
        public static SuperVector operator -(SuperVector left, int right)
        {
            int[] result = new int[left.Value.Length];
            fixed (int* ptrLeft = left.Value, resultPtr = result)
            {
                int limit = left.Value.Length - (left.Value.Length & 7);
                int i = 0;
                Vector256<int> target = Vector256.Create(right);
                for (; i < limit; i += _vsize)
                {
                    Avx2.Store(resultPtr + i, *(Vector256<int>*)(ptrLeft + i) - target);
                }
                for (; i < left.Value.Length; i++)
                {
                    *(resultPtr + i) = *(ptrLeft + i) - right;
                }
            }
            return result;
        }
        public static SuperVector operator -(int left, SuperVector right) => right - left;
        public static SuperVector operator *(SuperVector left, SuperVector right)
        {
            if (left.Value.Length != right.Value.Length)
                throw new ArgumentException("向量大小不一致");

            int[] result = new int[left.Value.Length];
            fixed (int* ptrLeft = left.Value, ptrRight = right.Value, resultPtr = result)
            {
                int limit = left.Value.Length - (left.Value.Length & 7);
                int i = 0;
                for (; i < limit; i += _vsize)
                {
                    Avx2.Store(resultPtr + i, *(Vector256<int>*)(ptrLeft + i) * *(Vector256<int>*)(ptrRight + i));
                }
                for (; i < left.Value.Length; i++)
                {
                    *(resultPtr + i) = *(ptrLeft + i) * *(ptrRight + i);
                }
            }
            return result;
        }
        public static SuperVector operator *(SuperVector left, int right)
        {
            int[] result = new int[left.Value.Length];
            fixed (int* ptrLeft = left.Value, resultPtr = result)
            {
                int limit = left.Value.Length - (left.Value.Length & 7);
                int i = 0;
                Vector256<int> target = Vector256.Create(right);
                for (; i < limit; i += _vsize)
                {
                    Avx2.Store(resultPtr + i, *(Vector256<int>*)(ptrLeft + i) * target);
                }
                for (; i < left.Value.Length; i++)
                {
                    *(resultPtr + i) = *(ptrLeft + i) * right;
                }
            }
            return result;
        }
        public static SuperVector operator *(int left, SuperVector right) => right * left;
        public static SuperVector operator /(SuperVector left, SuperVector right)
        {
            if (left.Value.Length != right.Value.Length)
                throw new ArgumentException("向量大小不一致");

            int[] result = new int[left.Value.Length];
            fixed (int* ptrLeft = left.Value, ptrRight = right.Value, resultPtr = result)
            {
                int limit = left.Value.Length - (left.Value.Length & 7);
                int i = 0;
                for (; i < limit; i += _vsize)
                {
                    Avx2.Store(resultPtr + i, *(Vector256<int>*)(ptrLeft + i) / *(Vector256<int>*)(ptrRight + i));
                }
                for (; i < left.Value.Length; i++)
                {
                    *(resultPtr + i) = *(ptrLeft + i) / *(ptrRight + i);
                }
            }
            return result;
        }
        public static SuperVector operator /(SuperVector left, int right)
        {
            int[] result = new int[left.Value.Length];
            fixed (int* ptrLeft = left.Value, resultPtr = result)
            {
                int limit = left.Value.Length - (left.Value.Length & 7);
                int i = 0;
                Vector256<int> target = Vector256.Create(right);
                for (; i < limit; i += _vsize)
                {
                    Avx2.Store(resultPtr + i, *(Vector256<int>*)(ptrLeft + i) / target);
                }
                for (; i < left.Value.Length; i++)
                {
                    *(resultPtr + i) = *(ptrLeft + i) / right;
                }
            }
            return result;
        }
        public static SuperVector operator /(int left, SuperVector right) => right / left;
        public static bool operator ==(SuperVector left, SuperVector right) => Equals(left, right);
        public static bool operator !=(SuperVector left, SuperVector right) => !Equals(left, right);
        public static implicit operator SuperVector(int[] value) => new SuperVector(value);
        public static explicit operator int[](SuperVector value) => value._value;

        public override string ToString() => string.Join(",", Value);
    }
}
